#!/usr/bin/env python3
# -*- coding:UTF-8 -*-

import sys
import os
import argparse
import re
import time

import AutoExecUtils


class REMatcher(object):
    def __init__(self, matchstring):
        self.matchstring = matchstring

    def match(self, regexp):
        self.rematch = re.match(regexp, self.matchstring, re.IGNORECASE)
        return bool(self.rematch)

    def search(self, regexp):
        self.rematch = re.search(regexp, self.matchstring, re.IGNORECASE)
        return bool(self.rematch)

    def group(self, i):
        return self.rematch.group(i)


def parseTimeWinDefine(timeWinDefine):
    hasError = False
    timeWins = []
    winType = "ByTime"
    year = None
    month = None
    day = None
    weekDay = None

    m = REMatcher(timeWinDefine)
    if m.match("^WeekDay"):
        if m.match("^WeekDay\s*(\d+)\s"):
            weekDay = int(m.group(1))
            if weekDay < 1 or weekDay > 7:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: WeekDay must between 1 and 7.")
            weekDay = weekDay - 1
            winType = "ByWeekDay"
        else:
            hasError = True
            print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
            print("INFO: Format example: WeekDay 3 19:00-21:00 22:00-24:00")

    elif m.match("^MonthDay"):
        if m.match("^MonthDay\s*(\d{1,2})-(\d{1,2})\s"):
            month = int(m.group(1))
            day = int(m.group(2))
            if month < 1 or month > 12:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: Month must between 1 and 12.")
            if day < 1 or day > 31:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: Month day must between 1 and 31.")
            winType = "ByMonthDay"
        else:
            hasError = True
            print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
            print("INFO: Format example: MonthDay 08-15 19:00-21:00 22:00-24:00")
    elif m.match("^Date"):
        if m.match("^Date\s*(\d{4})-(\d{1,2})-(\d{1,2})"):
            year = int(m.group(1))
            month = int(m.group(2))
            day = int(m.group(3))
            if month < 1 or month > 12:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: Month must between 1 and 12.")
            if day < 1 or day > 31:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: Month day must between 1 and 31.")

            try:
                time.strptime("%d-%02d-%02d" % (year, month, day), "%Y-%m-%d")
            except Exception as err:
                hasError = True
                print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
                print("INFO: " + str(err))
            winType = "ByDate"
        else:
            hasError = True
            print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
            print("INFO: Format example: Date 2060-08-15 19:00-21:00 22:00-24:00")

    for f in re.finditer("(\d{2}):(\d{2})-(\d{2}):(\d{2})", timeWinDefine):
        startHour = int(f.group(1))
        startMinute = int(f.group(2))
        endHour = int(f.group(3))
        endMinute = int(f.group(4))

        if startHour < 0 or startHour > 24 or endHour < 0 or endHour > 24:
            hasError = True
            print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
            print("INFO: Hour must between 0 and 24.")
        if startMinute < 0 or startMinute > 60 or endMinute < 0 or endMinute > 60:
            hasError = True
            print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
            print("INFO: Minute must between 0 and 60.")

        timeWins.append(
            {
                "WinType": winType,
                "DefineLine": timeWinDefine,
                "Year": year,
                "Month": month,
                "Day": day,
                "WeekDay": weekDay,
                "StartHour": startHour,
                "StartMinute": startMinute,
                "EndHour": endHour,
                "EndMinute": endMinute,
            }
        )

    if len(timeWins) == 0:
        print("ERROR: Malform time windows defined: %s" % (timeWinDefine))
        print("Examples:")
        print("    20:00-21:00 23:00-03:00")
        print("    WeekDay 7 20:00-21:00 23:00-06:00")
        print("    MonthDay 02-18 23:00-06:00")
        print("    Date 2060-12-28 22:00-06:00")

    if hasError:
        raise Exception("Malform time windows defined")

    return timeWins


def isTimeInTimeWin(timeWins):
    nowTime = time.localtime()

    isInTimeWin = False

    nowMinutes = nowTime.tm_hour * 60 + nowTime.tm_min

    for timeWin in timeWins:
        startMinutes = 0
        startMinutes = timeWin["StartHour"] * 60 + timeWin["StartMinute"]
        endMinutes = 1440
        endMinutes = timeWin["EndHour"] * 60 + timeWin["EndMinute"]

        if timeWin["WinType"] == "ByDate":
            nowEpoch = time.time()
            startTime = 0
            endTime = 0
            if endMinutes >= startMinutes:
                startTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d"
                        % (
                            timeWin["Year"],
                            timeWin["Month"],
                            timeWin["Day"],
                            timeWin["StartHour"],
                            timeWin["StartMinute"],
                        ),
                        "%Y-%m-%d %H:%M",
                    )
                )
                endTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d" % (timeWin["Year"], timeWin["Month"], timeWin["Day"], timeWin["EndHour"], timeWin["EndMinute"]),
                        "%Y-%m-%d %H:%M",
                    )
                )
            else:
                startTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d"
                        % (
                            timeWin["Year"],
                            timeWin["Month"],
                            timeWin["Day"],
                            timeWin["StartHour"],
                            timeWin["StartMinute"],
                        ),
                        "%Y-%m-%d %H:%M",
                    )
                )
                endTime = (
                    time.mktime(
                        time.strptime(
                            "%d-%02d-%02d %02d:%02d"
                            % (
                                timeWin["Year"],
                                timeWin["Month"],
                                timeWin["Day"],
                                timeWin["EndHour"],
                                timeWin["EndMinute"],
                            ),
                            "%Y-%m-%d %H:%M",
                        )
                    )
                    + 86400
                )
            if nowEpoch >= startTime and nowEpoch <= endTime:
                isInTimeWin = True
        elif timeWin["WinType"] == "ByMonthDay":
            nowEpoch = time.time()
            startTime = 0
            endTime = 0
            if endMinutes >= startMinutes:
                startTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d"
                        % (
                            nowTime.tm_year,
                            timeWin["Month"],
                            timeWin["Day"],
                            timeWin["StartHour"],
                            timeWin["StartMinute"],
                        ),
                        "%Y-%m-%d %H:%M",
                    )
                )
                endTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d" % (nowTime.tm_year, timeWin["Month"], timeWin["Day"], timeWin["EndHour"], timeWin["EndMinute"]),
                        "%Y-%m-%d %H:%M",
                    )
                )
            else:
                startTime = time.mktime(
                    time.strptime(
                        "%d-%02d-%02d %02d:%02d"
                        % (
                            nowTime.tm_year,
                            timeWin["Month"],
                            timeWin["Day"],
                            timeWin["StartHour"],
                            timeWin["StartMinute"],
                        ),
                        "%Y-%m-%d %H:%M",
                    )
                )
                endTime = (
                    time.mktime(
                        time.strptime(
                            "%d-%02d-%02d %02d:%02d"
                            % (
                                nowTime.tm_year,
                                timeWin["Month"],
                                timeWin["Day"],
                                timeWin["EndHour"],
                                timeWin["EndMinute"],
                            ),
                            "%Y-%m-%d %H:%M",
                        )
                    )
                    + 86400
                )
            if nowEpoch >= startTime and nowEpoch <= endTime:
                isInTimeWin = True
        elif timeWin["WinType"] == "ByWeekDay":
            if endMinutes >= startMinutes:
                if timeWin["WeekDay"] != nowTime.tm_wday:
                    continue
                if nowMinutes >= startMinutes and nowMinutes <= endMinutes:
                    isInTimeWin = True
            else:
                if timeWin["WeekDay"] != nowTime.tm_wday and (timeWin["WeekDay"] + 1) % 7 != nowTime.tm_wday:
                    continue
                if nowMinutes >= startMinutes or nowMinutes <= endMinutes:
                    isInTimeWin = True
        elif timeWin["WinType"] == "ByTime":
            if endMinutes > startMinutes:
                if nowMinutes >= startMinutes and nowMinutes <= endMinutes:
                    isInTimeWin = True
            else:
                if nowMinutes >= startMinutes or nowMinutes <= endMinutes:
                    isInTimeWin = True

        if isInTimeWin:
            # print("FINE: TimeWindow:%s is matched." % (timeWin["DefineLine"]))
            return timeWin


if __name__ == "__main__":
    exitCode = 0

    parser = argparse.ArgumentParser()
    parser.add_argument("--wait2fullfill", default="0", help="If wait time to full fill time windows.\n")
    parser.add_argument("--timewindow", default="", help="Time windows define")
    parser.add_argument(
        "privwhilelist",
        nargs=argparse.REMAINDER,
        help="White list privileges",
    )

    args = parser.parse_args()
    timeWinsDef = args.timewindow
    waitTime2Fullfilled = int(args.wait2fullfill)

    timeWinLines = []
    timeWins = []

    # 检查特权
    whitelistDict = {}
    for privilege in args.privwhilelist:
        whitelistDict[privilege] = 1

    hasPrivilege = 0
    try:
        userUuid = os.getenv("AUTOEXEC_USER")
        if userUuid is not None:
            privileges = AutoExecUtils.callNativeApi(
                "/neatlogic/api/rest/user/get",
                {"userUuid": userUuid},
            )
            userAuthList = privileges.get("userAuthList")
            if userAuthList is not None:
                for userAuth in userAuthList:
                    if whitelistDict.get(userAuth.get("auth")) == 1:
                        hasPrivilege = 1
                        break
    except Exception as err:
        print("ERROR: Get user {} privileges failed, {}".format(userUuid, err))
        exit(-1)
    if hasPrivilege == 1:
        sys.exit(0)

    try:
        for timeWinDefine in timeWinsDef.split("\\n"):
            timeWinDefine = timeWinDefine.strip()
            if timeWinDefine != "":
                timeWinLines.append(timeWinDefine)
                timeWins.extend(parseTimeWinDefine(timeWinDefine))
    except Exception as err:
        print("ERROR: %s" % (str(err)))
        sys.exit(2)

    matchedTimeWin = isTimeInTimeWin(timeWins)
    if waitTime2Fullfilled == 1:
        if matchedTimeWin is None:
            print("INFO: Wait to full fill the time windows.\n")
        while matchedTimeWin is None:
            time.sleep(60)
            matchedTimeWin = isTimeInTimeWin(timeWins)

    nowTimeStr = time.strftime("%Y-%m-%d %H:%M:%S")
    if matchedTimeWin is None:
        print("ERROR: Now time %s is not in defined time window." % (nowTimeStr))
        print("->" + "\n->".join(timeWinLines))
        sys.exit(-1)
    else:
        print("FINE: Now time %s is in maintaince time window %s." % (nowTimeStr, matchedTimeWin["DefineLine"]))
